package com.jianbing.http;

import com.alibaba.fastjson.JSONObject;
import com.jianbing.exception.ServiceException;
import com.jianbing.util.RandomUtils;
import com.jianbing.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.*;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;

import static org.apache.http.HttpStatus.SC_OK;

public class HttpAsynClient {
    private static String HIDDEN_IP = "0.0.0.0";
    private static Charset UTF_8 = Charset.forName("UTF-8");
    private static int defaultTimeout = 10000;
    private static final Log logger = LogFactory.getLog(HttpAsynClient.class);

    public static String doPost(String url, Map<String, String> params) throws Exception {
        return doPost(url, params, defaultTimeout);
    }

    public static String doPost(String url, String jsonParameters) throws Exception {
        return doPost(url, jsonParameters, defaultTimeout, null);
    }

    public static String doPost(String url, HttpEntity entity) throws Exception {
        return doPost(url, entity, defaultTimeout, null);
    }

    private static String doPost(String url, HttpEntity entity, int timeout, String contentType) throws Exception {
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(timeout)
                .setConnectTimeout(timeout).build();
        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .build();
        try {
            httpclient.start();
            final HttpPost request = new HttpPost(url);
            if (StringUtils.isNotEmpty(contentType)) {
                request.setHeader("Content-Type", contentType);
            }
            //设置参数到请求对象中
            request.setEntity(entity);
            final CountDownLatch latch = new CountDownLatch(1);
            StringBuilder result = new StringBuilder();
            Future<HttpResponse> future = httpclient.execute(request, new FutureCallback<HttpResponse>() {
                @Override
                public void completed(final HttpResponse response) {
                    latch.countDown();
                }

                @Override
                public void failed(final Exception ex) {
                    latch.countDown();
                }

                @Override
                public void cancelled() {
                    latch.countDown();
                }

            });
            latch.await();
            return processResult(future, true);
        } catch (Exception e) {
            logger.error(e);
            throw e;
        } finally {
            try {
                httpclient.close();
            } catch (IOException e) {

            }
        }
    }

    public static String doPost(String url, Map<String, String> map, int timeout) throws Exception {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (map != null) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
            }
        }

        HttpEntity entity = new UrlEncodedFormEntity(nvps, UTF_8);
        return doPost(url, entity, timeout, null);
    }

    public static void doPost2(String url, Map<String, Object> map, int timeout) throws Exception {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (map != null) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getValue() instanceof Map) {
                    List<NameValuePair> nvps2 = new ArrayList<NameValuePair>();
                    Map<String, Object> map2 = (Map) entry.getValue();
                    for (Map.Entry<String, Object> entry2 : map2.entrySet()) {
                        nvps2.add(new BasicNameValuePair(entry2.getKey(), entry2.getValue().toString()));
                    }
                } else {
                    nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
                }
            }
        }

        HttpEntity entity = new UrlEncodedFormEntity(nvps, UTF_8);
        doPost(url, entity, timeout, null);
    }

    public static String doPost(String url, String jsonParameter, int timeout, String contentType) throws Exception {
        HttpEntity entity = null;
        if (!StringUtils.isEmpty(jsonParameter)) {
            entity = new StringEntity(jsonParameter, UTF_8);
        }
        return doPost(url, entity, timeout, contentType);
    }

    private static String processResult(Future<HttpResponse> future, boolean logContent) throws Exception {
        HttpResponse response = future.get();
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() != SC_OK) {
            throw new ServiceException("http status " + statusLine.getStatusCode());
        }

        BufferedReader reader = null;
        StringBuilder result = new StringBuilder();
        try {
            if (logContent) {
                reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    result.append(line + "\r\n");
                }
            }
            return result.toString();
        } catch (IOException e) {
            logger.error(e);
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
            }
        }
        return "";
    }

    /**
     * @param url
     * @return
     * @throws Exception
     */
    public static String doGet(String url) throws Exception {
        return doGet(url, defaultTimeout, false, true);
    }

    public static String doGet(String url, int timeout, boolean hiddenIP, boolean logContent) throws Exception {
        return doGet(url, timeout, null, -1, null, null, hiddenIP, logContent);
    }

    public static String doGet(String url, int timeout, final boolean logContent) throws Exception {
        return doGet(url, timeout, null, -1, null, null, true, logContent);
    }

    public static String doGet(String url, String proxyIp, int proxyPort) throws Exception {
        return doGet(url, defaultTimeout, proxyIp, proxyPort, null, null, true, false);
    }

    public static String doGet(String url, String proxyIp, int proxyPort, String proxyAccount, String proxyPass) throws Exception {
        return doGet(url, defaultTimeout, proxyIp, proxyPort, proxyAccount, proxyPass, true, false);
    }

    public static String doGet(String url, String proxyIp, int proxyPort, String proxyAccount, String proxyPass, boolean hiddenIP, boolean logFlag) throws Exception {
        return doGet(url, defaultTimeout, proxyIp, proxyPort, proxyAccount, proxyPass, hiddenIP, logFlag);
    }

    /**
     * 创建SSL安全连接
     *
     * @return
     */
    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {

                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }

                @Override
                public void verify(String host, SSLSocket ssl) throws IOException {
                }

                @Override
                public void verify(String host, X509Certificate cert) throws SSLException {
                }

                @Override
                public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
                }
            });
        } catch (GeneralSecurityException e) {
            logger.error(e);
        }
        return sslsf;
    }

    public static void doSSLGet(String url) throws Exception {
        doSSLGet(url, defaultTimeout, null, -1);
    }

    public static String doSSLGet(String url, int timeout, String proxyIp, int proxyPort) throws Exception {
        RequestConfig.Builder builder = RequestConfig.custom()
                .setSocketTimeout(timeout)
                .setConnectTimeout(timeout);

        if (!StringUtils.isEmpty(proxyIp) && proxyPort > 0) {
            builder.setProxy(new HttpHost(proxyIp, proxyPort));
        }

        RequestConfig requestConfig = builder.build();

        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setDefaultRequestConfig(requestConfig).build();
        try {
            final HttpGet request
                    = new HttpGet(url);
            CloseableHttpResponse response = httpClient.execute(request);
            BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuilder result = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                result.append(line + "\r\n");
            }
            return result.toString();

        } catch (Exception e) {
            throw e;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {

            }
        }
    }

    public static String doGet(String url, int timeout, String proxyIp, int proxyPort, String proxyAccount, String proxyPass, boolean hiddenIP, final boolean logContent) throws Exception {
        CredentialsProvider credsProvider = null;
        if (proxyAccount != null && proxyPass != null) {
            credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(
                    new AuthScope(proxyIp, proxyPort),
                    new UsernamePasswordCredentials(proxyAccount, proxyPass));
        }
        RequestConfig.Builder builder = RequestConfig.custom()
                .setSocketTimeout(timeout)
                .setConnectTimeout(timeout);

        if (!StringUtils.isEmpty(proxyIp) && proxyPort > 0) {
            builder.setProxy(new HttpHost(proxyIp, proxyPort));
            builder.setAuthenticationEnabled(true);
        }

        RequestConfig requestConfig = builder.build();
        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
                .setDefaultRequestConfig(requestConfig).setDefaultCredentialsProvider(credsProvider)
                .build();

        try {
            httpclient.start();
            final HttpGet request =
                    new HttpGet(url);
            if (hiddenIP) {
                HIDDEN_IP = RandomUtils.getRandom().nextInt(255) + "." + RandomUtils.getRandom().nextInt(255) + "."
                        + RandomUtils.getRandom().nextInt(255) + "." + RandomUtils.getRandom().nextInt(255);
                request.setHeader("X-Forwarded-For", HIDDEN_IP);
                request.setHeader("HTTP_X_FORWARDED_FOR", HIDDEN_IP);
                request.setHeader("HTTP_CLIENT_IP", HIDDEN_IP);
                request.setHeader("REMOTE_ADDR", HIDDEN_IP);
                request.setHeader("HTTP_VIA", HIDDEN_IP);
                request.setHeader("REMOTE_HOST", HIDDEN_IP);
                request.setHeader("User-Agent", userAgentList.get(RandomUtils.getRandom().nextInt(userAgentList.size())));
            }
            StringBuilder result = new StringBuilder();
            final CountDownLatch latch = new CountDownLatch(1);
            Future<HttpResponse> future = httpclient.execute(request, new FutureCallback<HttpResponse>() {
                @Override
                public void completed(final HttpResponse response) {
                    latch.countDown();
                }

                @Override
                public void failed(final Exception ex) {
                    latch.countDown();
                }

                @Override
                public void cancelled() {
                    latch.countDown();
                }

            });
            latch.await();
            return processResult(future, logContent);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                httpclient.close();
            } catch (IOException e) {

            }
        }
    }

    private static final List<String> userAgentList = new ArrayList();

    static {
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36");
    }

    public static void main(final String[] args) throws Exception {

        System.out.println(RandomUtils.getRandom().nextInt(userAgentList.size()));
        JSONObject object = new JSONObject();
        object.put("taskId", "111");

        //doPost("http://localhost:4821/JBServices/QueryShebao", object.toJSONString(), 1000000);
        //  doGet("http://localhost:8099/api/yzm",true);
        //  doGet("http://www.proxy360.cn/default.aspx", true);

        //  doGet("http://www.baidu.com/", "58.19.222.139", 3128);
   /*  Map map = new HashMap();
        map.put("cityId", "2");
        doPost("http://localhost:8099/api/yzm", map);

    }*/
        //    doGet(String url, String proxyIp, int proxyPort, String proxyAccount, String proxyPass)
        //  doGet("http://www.hngjj.net/see/see_two.htm","114.233.116.164",29584,"jianghejun","7lbzmxnv");
        //    doSSLGet("https://localhost:8051/", 600000, null, -1);
    }

}